/*
 * Authors: Matt Stenzel, Josh Harris, Jon Homan
 * IT 484 Fall 2008
 * */

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Resources;

namespace MahjongTest
{
    class Board
    {
        private CustomPanel firstSelectedPanel = null;
        private CustomPanel firstMatchedPanel = null;
        private CustomPanel secondMatchedPanel = null;
        private Point firstSelectedPoint;
        private Point secondSelectedPoint;
        private int firstSelectedLevel;
        private int secondSelectedLevel;

        private ArrayList boardLayout = null;
        private static Random rng = new Random();
        private ArrayList shuffledTileList = null;
        private ArrayList tilePicArray = new ArrayList();
        private int tileCount = 144;
        private Form1 form;
        private int i = 0;
        private int j = 3;

        public ArrayList BoardLayout
        {
            get { return boardLayout; }
            set { boardLayout = value; }
        }

        public Board(ArrayList boardLayout, Form1 form)
        {
            this.boardLayout = boardLayout;
            this.shuffledTileList = this.randomizeTiles(this.makeTileIDList());
            this.form = form;
        }

        /**
         * For debugging.
         */ 
        public void printBoardLayout()
        {
            foreach (ArrayList level in boardLayout)
            {
                foreach (Point point in level)
                {
                    Console.Write("(" + point.X + ", " + point.Y + ")");
                }
                Console.WriteLine("");
            }
        }

        /**
         * Displays the current board configuration.
         */ 
        public void showBoard(Panel backPanel)
        {
            int levelColor = 1;
            int i = 0;

            this.makeTilePicList();
            foreach (ArrayList level in boardLayout)
            {
                foreach (Point point in level)
                {
                    CustomPanel panel = new CustomPanel(point, levelColor, this, (int)shuffledTileList[i]); 
                    this.applyTilePictures(panel);
                    panel.BorderStyle = BorderStyle.FixedSingle;
                    backPanel.Controls.Add(panel);
                    panel.BringToFront();
                    i++;
                }
                levelColor++;
                Console.WriteLine("");
            }
        }

        public void undoAction()
        {
            // Add the position of the two tiles back into the correct level ArrayList
            ((ArrayList)boardLayout[firstSelectedLevel - 1]).Add(firstSelectedPoint);
            ((ArrayList)boardLayout[secondSelectedLevel - 1]).Add(secondSelectedPoint);
            firstMatchedPanel.Visible = true;
            secondMatchedPanel.Visible = true;
            form.decrementScore();

        }

        /**
         * Checks to see if the tile is first tile is selectable and
         * if the second tile is selectable and matches the value of the first tile.
         */ 
        public void checkMatchingForTiles(CustomPanel panel)
        {
            // No tile has been selected yet.
            if (firstSelectedPanel == null)
            {
                // If the tile returns true for either case then print status otherwise
                // change the border and add the selected tile as the first selected tile.
                if (hasTileOnBothSides(panel) || hasTileOnTop(panel))
                {
                    // Print status to status bar.
                    ((StatusStrip)(form.Controls.Find("statusStrip1", true)[0])).Items[0].Text = "Tile Blocked";
                    
                }
                else
                {
                    panel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
                    firstSelectedPanel = panel;
                }
            }
            else
            {
                // Checks first tile to make sure they are not the same tile.
                if (!firstSelectedPanel.Equals(panel))
                {
                    ((StatusStrip)(form.Controls.Find("statusStrip1", true)[0])).Items[0].Text = "Tiles Do Not Match";
                    // If the tile returns true for either case then print status otherwise
                    // check for match tile values.
                    if (hasTileOnBothSides(panel) || hasTileOnTop(panel))
                    {
                        // Print status to status bar.
                        ((StatusStrip)(form.Controls.Find("statusStrip1", true)[0])).Items[0].Text = "Tile Blocked";
                    }
                    else
                    {
                        // If tiles have matching values then remove both tiles and
                        // deselect the first tile.
                        if (firstSelectedPanel.MatchNum == panel.MatchNum)
                        {
                            // Save points to put into array of tile locations (undo function)
                            firstSelectedPoint = firstSelectedPanel.Location;
                            secondSelectedPoint = panel.Location;

                            // Save level of tiles to put into correct level array (undo function)
                            firstSelectedLevel = firstSelectedPanel.LevelColor;
                            secondSelectedLevel = panel.LevelColor;

                            // Save the panels themselves (undo function)
                            firstMatchedPanel = firstSelectedPanel;
                            secondMatchedPanel = panel;

                            removeTile(panel);
                            removeTile(firstSelectedPanel);

                            this.tileCount -= 2;
                            this.gameWon();

                            firstSelectedPanel.BorderStyle = BorderStyle.FixedSingle;

                            // Print status to status bar.
                            ((StatusStrip)(form.Controls.Find("statusStrip1", true)[0])).Items[0].Text = "Tiles Matched";
                            
                            // Deselect title
                            firstSelectedPanel = null;
                            form.Controls.Find("UndoBtn", true)[0].Enabled = true; 
  
                        }
                        else
                        {
                            // Deselect title
                            firstSelectedPanel.BorderStyle = BorderStyle.FixedSingle;
                            firstSelectedPanel = null;
                        }
                    }
                }
                else
                {
                    // Deselect title
                    firstSelectedPanel.BorderStyle = BorderStyle.FixedSingle;
                    firstSelectedPanel = null;
                }
                
            }
        }

        /**
         * Checks to see if either the left or right side does not have a tile touching it.
         */
        public bool hasTileOnBothSides(CustomPanel panel)
        {
            ArrayList level;

            int blockedOnRight = 0;
            int blockedOnLeft = 0;

            level = (ArrayList)boardLayout[panel.LevelColor - 1];

            foreach (Point point in level)
            {
                /*
                 Covers if tiles are next to each other or are next to each other and horizontally
                 offset by a half tile either way.
                */
                if (((point.X == (panel.Location.X - 50)) && (point.Y == panel.Location.Y)))
                {
                    blockedOnLeft++;
                } 
                else if(((point.X == (panel.Location.X + 50)) && (point.Y == panel.Location.Y)))
                {
                    blockedOnRight++;
                }
                else if ((point.X == (panel.Location.X - 50)) && (point.Y == (panel.Location.Y - 35)))
                {
                    blockedOnLeft++;
                }
                else if ((point.X == (panel.Location.X - 50)) && (point.Y == (panel.Location.Y + 35)))
                {
                    blockedOnLeft++;
                }
                else if ((point.X == (panel.Location.X + 50)) && (point.Y == (panel.Location.Y - 35)))
                {
                    blockedOnRight++;
                }
                else if ((point.X == (panel.Location.X + 50)) && (point.Y == (panel.Location.Y + 35)))
                {
                    blockedOnRight++;
                }
            }

            if (blockedOnLeft > 0 && blockedOnRight > 0)
            {
                return true;
            }
            return false;  
        }

        /**
         * Checks to see if the chosen tile has a tile on top of any part of it.
         */ 
        public bool hasTileOnTop(CustomPanel panel)
        {
            ArrayList level;

            if (panel.LevelColor >= boardLayout.Count)
            {
                return false;
            } 
            else 
            {
                level = (ArrayList)boardLayout[panel.LevelColor];
            }

            foreach (Point item in level)
            {
                /*
                    Covers if tiles are next to each other or are directly on top of each other, or they 
                    are on top of each other and offset by a half tile either way.
                */
                if (((item.X == panel.Location.X) && (item.Y == panel.Location.Y)) ||
                    ((item.X == (panel.Location.X - 25)) && (item.Y == (panel.Location.Y - 35))) ||
                    ((item.X == (panel.Location.X - 25)) && (item.Y == (panel.Location.Y + 35))) ||
                    ((item.X == (panel.Location.X + 25)) && (item.Y == (panel.Location.Y - 35))) ||
                    ((item.X == (panel.Location.X + 25)) && (item.Y == (panel.Location.Y + 35))) ||
                    ((item.X == panel.Location.X) && (item.Y == (panel.Location.Y - 35))) ||
                    ((item.X == panel.Location.X) && (item.Y == (panel.Location.Y + 35))) ||
                    ((item.X == (panel.Location.X - 25)) && (item.Y == panel.Location.Y)) ||
                    ((item.X == (panel.Location.X + 25)) && (item.Y == panel.Location.Y)))
                {
                    return true;
                }
            }
            return false;
        }

        /**
         * Removes a tile from the array and sets its associated panel's visibility to false (no longer displayed)
         */ 
        public bool removeTile(CustomPanel panel)
        {
            int i = 0;
            foreach (ArrayList level in boardLayout)
            {
                i++;
                foreach(Point item in level)
                {
                    if ((item.X == panel.Location.X && item.Y == panel.Location.Y) && (i == panel.LevelColor))
                    {
                        level.Remove(item);
                        form.incrementScore();
                        panel.Visible = false;
                        return true;
                    }
                }
            }
            return false;
        }

        /*
         This method creates an ArrayList of numbers that will represent a matching number.
         There are 36 distinct matching sets(i.e. 1 of Bamboo).  Each of these sets has 4 tiles
         that can be matched.
         */
        private ArrayList makeTileIDList()
        {
            ArrayList tileID = new ArrayList();
            
        	for (int i = 0; i < 4; i++) 
            {
			    // Chinese Character Tile set up
			    for (int j = 0; j < 9; j++) 
                {
				    tileID.Add(j);
                }
			}

		    for (int i = 0; i < 4; i++) 
            {
			    // Dot Character Tile set up
			    for (int j = 9; j < 18; j++) 
                {
                    tileID.Add(j);
			    }
		    }
    		
		    for (int i = 0; i < 4; i++) 
            {
			    // Bamboo Character Tile set up
			    for (int j = 18; j < 27; j++) 
                {
                    tileID.Add(j);
			    }
		    }
    		
		    for (int i = 0; i < 4; i++) 
            {
                // Dragon Character Tile set up White / Green / Red
                for (int j = 27; j < 30; j++)
                {
                    tileID.Add(j);
                }
		    }

            // Flowers 4 of them, trickey because they should be the same value but need different pictures
            for (int i = 0; i < 4; i++)
            {
                tileID.Add(30);
            }

            // Seasons 4 of them, trickey because they should be the same value but need different pictures
            for (int i = 0; i < 4; i++)
            {
                tileID.Add(31);
            }

            for (int i = 0; i < 4; i++)
            {
                // Wind Character Tile set up
                for (int j = 38; j < 42; j++)
                {
                    tileID.Add(j);
                }
            }

            Console.Write("TILE LIST SIZE: " + tileID.Count);

            return tileID;
        }

        /*
         This method takes an ArrayList and shuffles its contents to randomize the tiles.
        */
        private ArrayList randomizeTiles(ArrayList tileIDList)
        {
            for (int i = tileIDList.Count - 1; i > 0; i--)
            {
                int swapIndex = rng.Next(i+1);
                if (swapIndex != i)
                {
                    object tmp = tileIDList[swapIndex];

                    tileIDList[swapIndex] = tileIDList[i];

                    tileIDList[i] = tmp;

                }
            }
                return tileIDList;
        }

        /**
         * This associates the pictures with the appropriate tiles.
         */
        private void applyTilePictures(CustomPanel panel)
        {
            // Flowers have different images but the same value (30).
            if (panel.MatchNum == 30) 
            {
                int flower = panel.MatchNum + i;
                panel.BackgroundImage = (Bitmap)tilePicArray[flower];
                i++;
            }
            // Season have different images but the same value (31)
            else if (panel.MatchNum == 31) 
            {
                int season = panel.MatchNum + j;
                panel.BackgroundImage = (Bitmap)tilePicArray[season];
                j++;
            }
            else 
            {
                panel.BackgroundImage = (Bitmap)tilePicArray[panel.MatchNum];
            }
            
            
        }

        /**
         * Creates a list of pictures for the tiles.
         */ 
        private void makeTilePicList()
        {
            ResXResourceReader rsxr = new ResXResourceReader("./Properties/Resources.resx");
            // Iterate through the resources and so some action
            int i = 0;
            foreach (DictionaryEntry d in rsxr)
            {
                tilePicArray.Add(d.Value);
                i++;
            }

        }

        /**
         * Checks to see if all tiles have been removed and if the game has been won.
         */
        public void gameWon()
        {
            if(this.tileCount == 0)
            {
                form.winner();
            }
        }
    }

}
